//
// Created by Administrator on 2015/5/22.
//

#pragma once

#include <string>
#include <unordered_map>

#include "../es.h"
#include "../es_log.h"
#include "lua.hpp"

namespace esvr {

  static void get_stack_args(lua_State* L, std::string& result){
    int argv = lua_gettop(L);
    while(argv-- > 0){
      switch(lua_type(L, -1)){
        case LUA_TNONE:
        case LUA_TNIL:
          result = "nil " + result;
          break;
        case LUA_TBOOLEAN:
        case LUA_TNUMBER:
        case LUA_TSTRING:
        result = lua_tostring(L, -1) + (" " + result);
        break;
        case LUA_TTABLE:
        result = "tabel " + result;
        break;
        case LUA_TFUNCTION:
        result = "function " + result;
        break;
        case LUA_TUSERDATA:
        result = "userdata " + result;
        break;
        case LUA_TTHREAD:
        result = "thread " + result;
        break;
      }
      lua_pop(L, 1);
    }
  }

  static int l_my_print(lua_State* L){
    lua_getglobal(L, "#fd#");
    socket_t fd = lua_tonumber(L, -1);
    lua_pop(L, 1);
    std::string result;
    get_stack_args(L, result);
    if(result.size() > 0){
      IOManager::get_instance().send(fd, result.c_str(), result.size());
    }
    IOManager::get_instance().send(fd, "\n", 1);
    return 0;
  }

  class LuaEnvEventHandler : public EventListener<ConnectedEvent>,
  public EventListener<ClosedEvent>,
  public EventListener<ReadEvent>,
  public EventListener<WriteEvent>,
  public Creator<LuaEnvEventHandler> {
  public:
    virtual void on_event(const ConnectedEvent *event) override {
      lua_State *L = ::luaL_newstate();
      ::luaopen_base(L);
      lua_pushnumber(L, event->fd());
      lua_setglobal(L, "#fd#");
      lua_pop(L, 1);
      lua_getglobal(L, "_G");
      lua_register(L, "print", l_my_print);
      lua_pop(L, 1);
      this->m_client_2_states[event->fd()] = L;
    }

    virtual void on_event(const ClosedEvent *event) override {
      socket_t fd = event->fd();
      lua_State *L = this->m_client_2_states[fd];
      lua_close(L);
      this->m_client_2_states.erase(fd);
    }

    virtual void on_event(const ReadEvent *event) override {
      socket_t fd = event->fd();
      int len = event->len();
      while(len > 0 && (event->data()[len - 1] == '\r' || event->data()[len-1] == '\n')) --len;
      std::string data(event->data(), len);
      if (!strncmp("exit", data.c_str(), 4) ||
      !strncmp("quit", data.c_str(), 4)) {
        IOManager::get_instance().send(fd, "bye\n", 4);
        IOManager::get_instance().force_close(fd);
        return;
      }

      lua_State *L = this->m_client_2_states[fd];
      log_debug("process data len[%d], content[%s]", data.size(), data.c_str());
      int ret = luaL_dostring(L, data.c_str());
      if (ret) {
        log_error("error luaL_dostring returned:%d", ret);
      }
      std::string result;
      get_stack_args(L, result);
      if(result.size() > 0){
        IOManager::get_instance().send(fd, result.c_str(), result.size());
        IOManager::get_instance().send(fd, "\nesvr_lua>", 10);
      }
      else{
        IOManager::get_instance().send(fd, "esvr_lua>", 9);
      }
    }

    virtual void on_event(const WriteEvent *event) override {
    }

  public:
    LuaEnvEventHandler() {}

  private:
    std::unordered_map<socket_t, lua_State *> m_client_2_states;
  };
};
